package entity;

import constant.Constant;
import controller.protocol.AlternatingMessageProtocol;
import controller.protocol.GameProtocol;
import util.SocketUtil;

import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

public class NegoRoom implements Serializable{

	private int roomNum;
	private int lastSpeakerNum;
	//private ServerSocket server;
	private List<Socket> clients;
	private Map<String, Boolean> connectedAddr;
	private int onlineClients;
	private NegoStatue negoStatue;
	private GameProtocol protocol;
	
	public NegoRoom(int roomNum){
		this. roomNum = roomNum;
		this.lastSpeakerNum = 0;
		//this.server = server;
		this.clients = new ArrayList<Socket>();
		this.connectedAddr = new LinkedHashMap<>();
		this.negoStatue = NegoStatue.PreNego;
		this.protocol = new AlternatingMessageProtocol(this);
	}

	/**
	 * 添加一个新的客户端到该房间内
	 * @param socket
	 * @return 该客户端在该房间内的编号
	 */
	public int addClient(Socket socket){
		int clientNum = 0;
		switch (this.getNegoStatue()){
			case PreNego:
				if(clients.size() == Constant.NUM_OF_PLAYER_EACH_ROOM){
					for(int i = 0; i<clients.size(); i++){
						if(clients.get(i)==null){
							//System.out.println("null client:    "+i);
							addClientToSpecPostion(i, socket);
							clientNum = i;
							break;
						}
					}
				}else{
					clients.add(socket);
					String remoteAddr = SocketUtil.getSocketAddr(socket);
					connectedAddr.put(remoteAddr, true);
					clientNum = clients.size() - 1;
				}
				break;
			case Pause:

				break;
		}
		this.setOnlineClients(this.getOnlineClients() + 1);
		this.updateNegoStatue();
		return clientNum;
	}

	public void addClientToSpecPostion(int clientNum, Socket client){
		clients.set(clientNum, client);
		int i = 0;
		Map<String, Boolean> map = new LinkedHashMap<>();
		map.put(SocketUtil.getSocketAddr(client), true);
		String oldAddr = "";
		for(String item: connectedAddr.keySet()){
			if(i == clientNum){
				oldAddr = item;
			}else if(i>clientNum){
				map.put(item, connectedAddr.get(item));
			}
			i++;
		}
		connectedAddr.remove(oldAddr);
		for(String str: map.keySet()){
			connectedAddr.remove(str);
		}
		//System.out.println("map:    "+map);
		connectedAddr.putAll(map);
	}

	/**
	 * 客户端退出
	 * 1，把该连接的状态置为false
	 * 2，把该客户端的socket置为null
	 * 3，在线的客户端数量减一
	 * @param client
	 */
	public void clientExit(Socket client){

		String remoteAddr = SocketUtil.getSocketAddr(client);
		connectedAddr.put(remoteAddr, false);
		int i = 0;
		for(String addr: connectedAddr.keySet()){
			if(addr.equals(remoteAddr)){
				clients.set(i, null);
				break;
			}
			i++;
		}
		this.onlineClients--;
		this.updateNegoStatue();
		//System.out.println("NegoStatue:  "+this.getNegoStatue());
	}

	/**
	 * 重新连接
	 * @param client 重新连接的socket，要求该socket的地址必须在该房间内
	 * @return 重新连接后的客户端在该房间的编号
	 */
	public int reconnect(Socket client){
		String addr = SocketUtil.getSocketAddr(client);
		int num = 0;
		for(String item: connectedAddr.keySet()) {
			if (item.equals(addr)) {
				clients.set(num, client);
				connectedAddr.put(item, true);
				onlineClients++;
				break;
			} else {
				num++;
			}
		}
		updateNegoStatue();
		return num;
	}

	/**
	 *添加一个客户端到该房间内
	 * 1，判断该客户端以前是否连接到该房间
	 * 2，如果以前连接过，则直接执行重新连接
	 * 3，如果以前没有连接过，则执行添加新的客户端到该房间
	 * @param client
	 * @return 客户端在该房间内的编号
	 */
	public int connect(Socket client){
		int num = 0;
		if(isClientInThisRoom(client)){
			num = reconnect(client);
		}else{
			num = addClient(client);
		}
		return num;
	}
	
	public boolean isClientInThisRoom(String remoteSocketAddr){
		/*for(Socket socket: clients){
			if(remoteSocketAddr.equals(socket.getRemoteSocketAddress().toString())){
				return true;
			}
		}
		return false;*/
		return connectedAddr.containsKey(remoteSocketAddr);
	}
	
	public boolean isClientInThisRoom(Socket client){
		return connectedAddr.containsKey(SocketUtil.getSocketAddr(client));
	}
	
	public int getRoomNum() {
		return roomNum;
	}
	public void setRoomNum(int roomNum) {
		this.roomNum = roomNum;
	}
	/*public ServerSocket getServer() {
		return server;
	}
	public void setServer(ServerSocket server) {
		this.server = server;
	}*/
	public List<Socket> getClients() {
		return clients;
	}
	public void setClients(List<Socket> clients) {
		this.clients = clients;
	}

	public int getOnlineClients() {
		return onlineClients;
	}

	public void setOnlineClients(int onlineClients) {
		this.onlineClients = onlineClients;
	}

	public void setNegoStatue(NegoStatue negoStatue){
		this.negoStatue = negoStatue;
	}
	public NegoStatue getNegoStatue(){
		return this.negoStatue;
	}

	public int getLastSpeakerNum() {
		return lastSpeakerNum;
	}

	public void setLastSpeakerNum(int lastSpeakerNum) {
		this.lastSpeakerNum = lastSpeakerNum;
	}

	public GameProtocol getProtocol() {
		return protocol;
	}

	public void setProtocol(GameProtocol protocol) {
		this.protocol = protocol;
	}

	public int getClientNum(Socket client){
		int clientNum = 0;
		String addr = SocketUtil.getSocketAddr(client);
		for(String item: connectedAddr.keySet()){
			if(addr.equals(item)){
				break;
			}
			clientNum++;
		}
		return clientNum;
	}

	public void updateNegoStatue(){
		if(clients.size()== Constant.NUM_OF_PLAYER_EACH_ROOM){
			if(onlineClients == Constant.NUM_OF_PLAYER_EACH_ROOM){
				this.setNegoStatue(NegoStatue.OnNego);
			}else{
				if(this.negoStatue == NegoStatue.OnNego){
					this.setNegoStatue(NegoStatue.Pause);
				}else if(this.negoStatue == NegoStatue.Pause){
					this.negoStatue = NegoStatue.Pause;
				}else{
					this.setNegoStatue(NegoStatue.PreNego);
				}
			}
		}else{
			this.setNegoStatue(NegoStatue.PreNego);
		}
	}

	public RoomInfo generateRoomInfo(){
		return new RoomInfo(this.roomNum, this.clients.size(), this.onlineClients, this.lastSpeakerNum, this.negoStatue);
	}
}
